﻿#if SRP_URP
#if UNITY_EDITOR
#define PROFILER
#endif
using Devil;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using UnityEngine;
using UnityEngine.Rendering;
using UnityEngine.Rendering.Universal;

namespace GameToolkit
{
    [System.Obsolete("不再支持， 使用 CustomVolumeRenderer 和 BaseVolumeComponent 代替", false)]
    public class URPPostProcessPass : ScriptableRenderPass
    {
        CustomPostProcessPassID passId;
        CustomVolumeComponent component;
        internal CustomPostProcessing urpFeature;
        internal ScriptableRenderer urpRenderer;
        internal Camera currentCamera;
        internal bool isLastPass;
        //internal bool isPreviewCamera;
        public int id => passId.passIndex;
        public int renderQ => passId.renderQ;
        public CustomPostProcessing UsingFeature { get { return urpFeature; } }
        public ScriptableRenderer UsingRenderer { get { return urpRenderer; } }
        public Camera CurrentCamera { get { return currentCamera; } }
        //public bool IsPreviewCamera { get { return isPreviewCamera; } }
#if PROFILER
        string profiler;
#endif
        public URPPostProcessPass()
        {
        }

        internal void SetComponent(CustomPostProcessPassID id, CustomVolumeComponent com, bool isLast)
        {
#if PROFILER
            if (this.component != com)
            {
                var type = com.GetType().Name;
                profiler = $"Post<{type}> #{id.passIndex}";
            }
#endif
            this.passId = id;
            this.component = com;
            this.isLastPass = isLast;
            renderPassEvent = id.renderEvent;
        }

        public override void Configure(CommandBuffer cmd, RenderTextureDescriptor cameraTextureDescriptor)
        {
            component.ConfigPass(this, cmd, cameraTextureDescriptor);
        }

        public override void Execute(ScriptableRenderContext context, ref RenderingData renderingData)
        {
#if PROFILER
            var cmd = CommandBufferPool.Get();
            cmd.BeginSample(profiler);
            context.ExecuteCommandBuffer(cmd);
#endif
            component.ExecutePass(this, context, ref renderingData);
#if PROFILER
            cmd.Clear();
            cmd.EndSample(profiler);
            context.ExecuteCommandBuffer(cmd);
            CommandBufferPool.Release(cmd);
#endif
        }

        public override void OnCameraCleanup(CommandBuffer cmd)
        {
            if (isLastPass)
                component.FrameCleanup(cmd);
        }

    }

    [System.Obsolete("将不再支持，使用 CustomVolumeRenderer 替换", false)]
    public class CustomPostProcessing : ScriptableRendererFeature
    {
        [SerializeField]
        Object[] m_AssetsLib;

        URPPostProcessPass[] mPostPasses;
        int mPassSize;
        List<System.Type> mComponentTypes;

        CustomVolumeComponent[] mActivatedComponents;
        int mActivatedCount;
        MinHeap<URPPostProcessPass> mActivePasses;

        HashSet<ICacheableVolumeComponent> mCacheables = new HashSet<ICacheableVolumeComponent>();

        public T GetAsset<T>([NotNull]string assetName) where T : Object
        {
            var len = m_AssetsLib == null ? 0 : m_AssetsLib.Length;
            for (int i = 0; i < len; i++)
            {
                if (m_AssetsLib[i] is T asset && assetName.EqualsIgnoreCase(asset.name))
                    return asset;
            }
            return null;
        }

        public T FindAsset<T>([NotNull]FilterDelegate<T> pattern) where T: Object
        {
            var len = m_AssetsLib == null ? 0 : m_AssetsLib.Length;
            for (int i = 0; i < len; i++)
            {
                if (m_AssetsLib[i] is T asset && pattern(asset))
                    return asset;
            }
            return null;
        }

        void GetComponentTypes()
        {
            var types = VolumeManager.instance.baseComponentTypeArray;
            if (types == null)
                return;
            if (mComponentTypes == null)
                mComponentTypes = new List<System.Type>();
            else
                mComponentTypes.Clear();
            var interfaceType = typeof(CustomVolumeComponent);
            //var itname = interfaceType.Name;
            for (int i = 0; i < types.Length; i++)
            {
                //var tp = types[i].GetInterface(itname);
                //if (tp == interfaceType)
                if (GlobalUtil.IsTypeInheritedFrom(types[i], interfaceType))
                {
                    mComponentTypes.Add(types[i]);
                }
            }
        }

        int IndexOfActivated(CustomVolumeComponent component)
        {
            for (int i = 0; i < mActivatedCount; i++)
            {
                if (component == mActivatedComponents[i])
                    return i;
            }
            return -1;
        }

        public override void Create()
        {
            if (mPostPasses == null)
                mPostPasses = new URPPostProcessPass[16];
            if (mActivatedComponents == null)
                mActivatedComponents = new CustomVolumeComponent[8];
            if (mActivePasses == null)
                mActivePasses = new MinHeap<URPPostProcessPass>(5,
                    (a, b) => a.renderQ < b.renderQ ? -1 : (a.renderQ > b.renderQ ? 1 : 0));
            mPassSize = 0;
            GetComponentTypes();
            //if (!isActive)
            //{
            for (int i = 0; i < mActivatedCount; i++)
            {
                var com = mActivatedComponents[i];
                mActivatedComponents[i] = null;
                if (com != null)
                {
                    com.OnBecomeDisabled(this);
                }
            }
            mActivatedCount = 0;
            foreach(var cacheable in mCacheables)
            {
                cacheable.ReleaseCacheableAssets();
            }
            mCacheables.Clear();
            //}
        }

        protected override void Dispose(bool disposing)
        {
            base.Dispose(disposing);
            for (int i = 0; i < mActivatedCount; i++)
            {
                var com = mActivatedComponents[i];
                mActivatedComponents[i] = null;
                if (com != null)
                    com.OnBecomeDisabled(this);
            }
            mActivatedCount = 0;
            foreach (var cacheable in mCacheables)
            {
                cacheable.ReleaseCacheableAssets();
            }
            mCacheables.Clear();
        }

        void GetPostProcessPasses(ScriptableRenderer renderer, ref RenderingData renderingData, ICollection<URPPostProcessPass> postProcessPass)
        {
            var cam = renderingData.cameraData;
            if (!cam.postProcessEnabled)
                return;
#if UNITY_EDITOR
            if (cam.isSceneViewCamera)
            {
                var scene = UnityEditor.SceneView.currentDrawingSceneView;
                if (scene != null && !scene.sceneViewState.imageEffectsEnabled)
                    return;
            }
#endif
            var addcam = cam.camera.GetUniversalAdditionalCameraData();
            var stack = addcam.volumeStack;// VolumeManager.instance.stack;
            mPassSize = 0;
            int activeCount = 0;
            for (int i = 0; i < mComponentTypes.Count; i++)
            {
                var com = stack.GetComponent(mComponentTypes[i]) as CustomVolumeComponent;
                if (com != null && com.IsActive())
                {
                    var passes = com.GetPasses();
                    var num = passes == null ? 0 : passes.Count;
                    var size = num + mPassSize;
                    //if (size == mPassSize)
                    //    continue;

                    var index = IndexOfActivated(com);
                    if (index == -1)
                    {
                        NativeArrayUtil.EnsureNativeSize(ref mActivatedComponents, mActivatedCount, mActivatedCount + 1);
                        if (mActivatedCount > activeCount)
                            mActivatedComponents[mActivatedCount] = mActivatedComponents[activeCount];
                        mActivatedComponents[activeCount] = com;
                        activeCount++;
                        mActivatedCount++;
                        com.OnBecomeEnabled(this);

                        if (com is ICacheableVolumeComponent cacheable)
                            mCacheables.Add(cacheable);
                    }
                    else if (index > activeCount)
                    {
                        mActivatedComponents[index] = mActivatedComponents[activeCount];
                        mActivatedComponents[activeCount] = com;
                        activeCount++;
                    }
                    else if (index == activeCount)
                    {
                        activeCount++;
                    }

                    //if (com is IExecutablePostProcessComponent ec)
                    //    ec.OnExecutePostProcess(this);
                    if (size > mPassSize)
                    {
                        NativeArrayUtil.EnsureNativeSize(ref mPostPasses, mPassSize, size);
                        var last = size - 1;
                        for (int pass = mPassSize; pass < size; pass++)
                        {
                            var p = mPostPasses[pass];
                            if (p == null)
                            {
                                p = new URPPostProcessPass();
                                mPostPasses[pass] = p;
                            }
                            p.urpFeature = this;
                            p.urpRenderer = renderer;
                            p.currentCamera = renderingData.cameraData.camera;
                            //p.isPreviewCamera = isPreview;
                            p.SetComponent(passes[pass - mPassSize], com, pass == last);
                            postProcessPass.Add(p);
                            //renderer.EnqueuePass(p);
                        }
                    }
                    mPassSize = size;

                }
            }
            for (int i = activeCount; i < mActivatedCount; i++)
            {
                var com = mActivatedComponents[i];
                if (com != null)
                    com.OnBecomeDisabled(this);
                mActivatedComponents[i] = null;
            }
            mActivatedCount = activeCount;
        }

        public override void AddRenderPasses(ScriptableRenderer renderer, ref RenderingData renderingData)
        {
            GetPostProcessPasses(renderer, ref renderingData, mActivePasses);
            //base.AddRenderPasses(renderer, ref renderingData);
            while(mActivePasses.Count > 0)
            {
                renderer.EnqueuePass(mActivePasses.RemoveTop());
            }
            //for (int i = 0; i < mActivePasses.Count; i++)
            //{
            //    renderer.EnqueuePass(mActivePasses[i]);
            //}
            //mActivePasses.Clear();
        }

    }
}

#endif